﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Globalization;
using System.Linq.Expressions;
using System.Data.Objects;
using Repositories.Interfaces;

namespace Repositories.Implementations
{
  public abstract class BaseRepository<T> : IBaseRepository<T>  where T : class
  {
    /// <summary>
    /// Gets the name of the entity set.
    /// </summary>
    /// <value>The name of the entity set.</value>
    protected abstract string EntitySetName
    {
      get;
    }
    /// <summary>
    /// Unit of work
    /// </summary>
    private UnitOfWork _unitOfWork;

    
    /// <summary>
    /// Initilize data context
    /// </summary>     

    public ASE_DatabaseEntities ASEDbContext
    {
      get
      {
        if ((ASE_DatabaseEntities)ObjectContextManager.Current == null)
        {
          ObjectContextManager.Init(new ASE_DatabaseEntities());
        }

        return (ASE_DatabaseEntities)ObjectContextManager.Current;
      }
    }

    public void Insert( T entity )
    {
      ASEDbContext.AddObject(EntitySetName, entity);
    }

    /// <summary>
    /// Deletes the specified workshop.
    /// </summary>
    /// <param name="entity">The workshop.</param>
    public void Delete( T entity )
    {
      ASEDbContext.DeleteObject(entity);
    }

    /// <summary>
    /// Updates the specified workshop.
    /// </summary>
    /// <param name="entity">The workshop.</param>
    public void Update(T entity)
    {
      object originalItem;

      EntityKey key = ASEDbContext.CreateEntityKey(EntitySetName, entity);

      if( ASEDbContext.TryGetObjectByKey( key, out originalItem ) )
      {
        ASEDbContext.ApplyPropertyChanges(EntitySetName, entity);
      }
    }

    /// <summary>
    /// Gets the query.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="predicate">The predicate.</param>
    /// <returns>Query Results.</returns>
    public IQueryable<T> GetQuery( Expression<Func<T, bool>> predicate )
    {
      return GetQuery().Where( predicate );
    }

    /// <summary>
    /// Gets the query.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>Query Results.</returns>
    public IQueryable<T> GetQuery()
    {
      return ASEDbContext.CreateQuery<T>(EntitySetName);
    }
    

    public void AttachTo(T entity)
    {
      ASEDbContext.AttachTo(EntitySetName, entity);
    }

    /// <summary>
    /// Deattaches to.
    /// </summary>
    /// <param name="combo">The entity.</param>
    public void DeattachTo(T combo)
    {
      ASEDbContext.Detach(combo);
    }

	/// <summary>
	/// Gets the query.
	/// </summary>
	/// <param name="sortParam">The predicate.</param>
	/// <param name="page">The page.</param>
	/// <param name="pageSize">Size of the page.</param>
	/// <param name="asc">if set to <c>true</c> [asc].</param>
	/// <param name="rowsCount">The rows count.</param>
	/// <returns>Query Results.</returns>
	/// <remarks></remarks>
	///  
	public IQueryable<T> GetQuery(Expression<Func<T, object>> sortParam, int page, int pageSize, bool asc, out long rowsCount)
	{
		var expresssionType = sortParam.Body.GetType();
		string propertyName = expresssionType == typeof(System.Linq.Expressions.UnaryExpression) ? ((MemberExpression)((UnaryExpression)sortParam.Body).Operand).Member.Name : ((MemberExpression)sortParam.Body).Member.Name;
		var items = ASEDbContext.CreateQuery<T>(EntitySetName).AsQueryable<T>();
		rowsCount = items.Count();
		return Sort(items, propertyName, asc, page, pageSize);
	}


	protected IQueryable<T> Sort(IQueryable<T> items, string propertyName, bool asc, int page, int pageSize)
	{
		var type = typeof(T);
		var expressionProperty = type.GetProperty(propertyName);
		var exPressionparameter = Expression.Parameter(type, "p");
		var propertyAccess = Expression.MakeMemberAccess(exPressionparameter, expressionProperty);
		var orderByExp = Expression.Lambda(propertyAccess, exPressionparameter);
		Expression resultExp;
		if (asc)
			resultExp = Expression.Call(typeof(Queryable), "OrderBy", new System.Type[] { type, expressionProperty.PropertyType }, items.Expression, Expression.Quote(orderByExp));
		else
			resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new System.Type[] { type, expressionProperty.PropertyType }, items.Expression, Expression.Quote(orderByExp));
		return items.AsQueryable().Provider.CreateQuery<T>(resultExp).Skip(page).Take(pageSize);
	}

	protected IQueryable<T> Sort(IQueryable<T> items, string propertyName, bool asc)
	{
		var type = typeof(T);
		var expressionProperty = type.GetProperty(propertyName);
		var exPressionparameter = Expression.Parameter(type, "p");
		var propertyAccess = Expression.MakeMemberAccess(exPressionparameter, expressionProperty);
		var orderByExp = Expression.Lambda(propertyAccess, exPressionparameter);
		Expression resultExp;
		if (asc)
			resultExp = Expression.Call(typeof(Queryable), "OrderBy", new System.Type[] { type, expressionProperty.PropertyType }, items.Expression, Expression.Quote(orderByExp));
		else
			resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new System.Type[] { type, expressionProperty.PropertyType }, items.Expression, Expression.Quote(orderByExp));
		return items.AsQueryable().Provider.CreateQuery<T>(resultExp);
	}


	public IQueryable<T> GetQuery(Expression<Func<T, object>> sortParam, bool asc, out long rowsCount)
	{
		var expresssionType = sortParam.Body.GetType();
		string propertyName = expresssionType == typeof(System.Linq.Expressions.UnaryExpression) ? ((MemberExpression)((UnaryExpression)sortParam.Body).Operand).Member.Name : ((MemberExpression)sortParam.Body).Member.Name;
		var items = ASEDbContext.CreateQuery<T>(EntitySetName).AsQueryable<T>();
		rowsCount = items.Count();
		return Sort(items, propertyName, asc);
	}

	public IQueryable<T> GetQuery(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> sortParam, int page, int pageSize, bool asc, out long rowsCount)
	{
		var expresssionType = sortParam.Body.GetType();
		string propertyName = expresssionType == typeof(System.Linq.Expressions.UnaryExpression) ? ((MemberExpression)((UnaryExpression)sortParam.Body).Operand).Member.Name : ((MemberExpression)sortParam.Body).Member.Name;
		var items = ASEDbContext.CreateQuery<T>(EntitySetName).AsQueryable<T>().Where(predicate);
		rowsCount = items.Count();
		return Sort(items, propertyName, asc, page, pageSize);

	}

	


	public IQueryable<T> GetQuery(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> sortParam, bool asc, out long rowsCount)
	{
		var expresssionType = sortParam.Body.GetType();
		string propertyName = expresssionType == typeof(System.Linq.Expressions.UnaryExpression) ? ((MemberExpression)((UnaryExpression)sortParam.Body).Operand).Member.Name : ((MemberExpression)sortParam.Body).Member.Name;
		var items = ASEDbContext.CreateQuery<T>(EntitySetName).AsQueryable<T>().Where(predicate);
		rowsCount = items.Count();
		return Sort(items, propertyName, asc);

	}
    /// <summary>
    /// Gets the unit of work.
    /// </summary>
    public UnitOfWork UnitOfWork
    {
      get
      {
        if (_unitOfWork == null)
        {
          _unitOfWork = new UnitOfWork(ASEDbContext);
        }
        return _unitOfWork;
      }
    }
  }
}
